#!/usr/bin/python
# -*- coding: utf-8 -*-
from PIL import Image

BREITE = 320
HOEHE = 240
HINTERGRUNDFARBE = 0

class Raytracer(object):

    def __init__(self, breite=BREITE, hoehe=HOEHE, hintergrundFarbe=HINTERGRUNDFARBE):
        self._breite = breite
        self._hoehe = hoehe
        self._hintergrundFarbe = hintergrundFarbe
        self._image = Image.new("RGB", (self._breite, self._hoehe))

    def calcRay(x, y):
        # Erst Kameraklasse fertig machen
        pass        

    def maleBild(self):
        for x in range(self._breite):
            for y in range(self._hoehe):
                ray = calcRay(x, y)
                maxdist = float('inf')
                color = self._hintergrundFarbe
                for objekt in objectlist:
                    hitdist = objekt.intersectionParameter(ray)
                    if hitdist:
                        if 0 < hitdist < maxdist:
                            maxdist = hitdist
                            color = objekt.colorAt(ray)
                
                
    def draw(self):
        self._image.save("raycastingPic.png")
        self._image.show()


class Color(object):
    # Farben am besten als Farbvektoren (255,255,255)
    pass
    
class Kamera(object):

    def __init__(self):
        # erst Punkt und Vektorklasse fertig machen
        pass

class Vektor(object):
    """homogenen Vektoren 2D"""

    def __init__(self, x, y, z):
        if z==0:
            self._x = float(x)
            self._y = float(y)
            self._z = float(z)
        else:
            raise AttributeError ("z Komponente ist NICHT Null -> kein Vektor!")

    # skalarprodukt
    def skalarprodukt(self, vektor):
        if isinstance(vektor, Vektor):            
            return self.x * vektor.x + self.y * vektor.y + self.z * vektor.z
        else:
            raise TypeError("vektor ist nicht vom Typ Vektor!")
 
    # kreuzprodukt
    def kreuzprodukt(self, vektor):
        if isinstance(vektor, Vektor):
            x = self.y * vektor.z - self.z * vektor.y
            y = self.z * vektor.x - self.x * vektor.z
            z = self.x * vektor.y - self.y * vektor.x
            return Vektor(x,y,z)
        else:
            raise TypeError("vektor ist nicht vom Typ Vektor!")

    
    
    def __repr__(self):
        return "(%s, %s, %s)" % (self.x, self.y, self.z)

    # Properties/getter setter
    def getx(self):
        return self._x
    def setx(self, wert):
        self._x = wert
    x = property(getx, setx, None, "x-Komponente")
    
    def gety(self):
        return self._y
    def sety(self, wert):
        self._y = wert
    y = property(gety, sety, None, "y-Komponente")
        
    def getz(self):
        return self._z
    def setz(self, wert):
        self._z = wert
    z = property(getz, setz, None, "z-Komponente")
    
class Punkt(object): # getestet
    """2D Homogene Punkte"""
    
    def __init__(self, x, y, z):
        if z != 0:            
            self._x = float(x)/z
            self._y = float(y)/z
            self._z = float(z)/z
        else:
            raise AttributeError ("z Komponente ist Null -> kein Punkt!!")

    def __sub__(self, anderes):
        if isinstance(anderes, Punkt) or isinstance(anderes, Vektor):
            x = self.x - anderes.x
            y = self.y - anderes.y
            z = self.z - anderes.z
            if z==0:
                return Vektor(x, y, z)
            else:
                return Punkt(x, y, z)
        else:
            raise TypeError("anderes ist nicht vom Typ Punkt oder Vektor!")
    
    def __add__(self, vektor):
        if isinstance(vektor, Vektor):
            x = self.x + vektor.x
            y = self.y + vektor.y
            z = self.z + vektor.z
            return Punkt(x, y, z)
        else:
            raise AttributeError("vektor ist nicht vom Typ Vektor!")

    def __repr__(self):
        return "(%s, %s, %s)" % (self.x, self.y, self.z)

    # Properties/getter setter
    def getx(self):
        return self._x
    def setx(self, wert):
        self._x = wert
    x = property(getx, setx, None, "x-Komponente")
    
    def gety(self):
        return self._y
    def sety(self, wert):
        self._y = wert
    y = property(gety, sety, None, "y-Komponente")
        
    def getz(self):
        return self._z
    def setz(self, wert):
        self._z = wert
    z = property(getz, setz, None, "z-Komponente")    
        

class Ray(object):
    def __init__(self, origin, direction):
        self.origin = origin # point
        self.direction = direction.normalized() # vector

    def __repr__(self):
        return 'Ray(%s,%s)' % (repr(self.origin), repr(self.direction))

    def pointAtParameter(self, t):
        return self.origin + self.direction.scale(t)



class Sphere(object):
    def __init__(self, center, radius):
        self.center = center # point
        self.radius = radius # scalar

    def __repr__(self):
        return 'Sphere(%s,%s)' % (repr(self.center), self.radius)

    def intersectionParameter(self, ray):
        co = self.center - ray.origin
        v = co.dot(ray.direction)
        discriminant = (self.radius * self.radius) - (co.dot(co) - v*v)
        if discriminant < 0:
            return None
        else:
            return v - math.sqrt(discriminant)

    def normalAt(self, p):
        return (p - self.center).normalized()


class Plane(object):
    def __init__(self, point, normal):
        self.point = point # point
        self.normal = normal.normalized() # vector

    def __repr__(self):
        return 'Plane(%s,%s)' % (repr(self.point), repr(self.normal))

if __name__ == '__main__':
    raytracer = Raytracer()
##    raytracer.draw()

    print "Führe Tests aus..."

    vz = Vektor(0,0,0)
    v = Vektor(1,2,3)
    v2 = Vektor(2,2,2)
    v3 = Vektor(4,3,0)
    v4 = Vektor(1,1,1)
    v5 = Vektor(1,2,1)
    v6 = Vektor(2,-1,3)
    
    # Addition testen
    assert(v+v2 == Vektor(3, 4, 5))
##
##    # Subtraktion testen
##    assert(v-v2 == Vektor(-1, 0, 1))
##
##    # Skalarprodukt testen
##    assert(v.dot(v2) == 12)
##    assert(v.dot(v2) == v2.dot(v))
##
##    # Länge testen
##    assert(v3.length() == 5)
##
##    # Division testen
##    assert(v3 / 2 == Vektor(2, 3/2.0, 0))
##
##    # Normalisierung testen
##    assert(v.normalized().length() == 1)
##
##    # Skalierung testen
##    assert(v.scale(5) == v*5)
##
##    # Vektorprodukt testen
##    assert(v2.cross(v4) == vz)
##    assert(v4.cross(v2) == vz)
##    assert(v5.cross(v6) == Vektor(7,-1,-5))
##
##    # Inverse testen
##    assert(v.inversed() == Vector(1, 0.5, 1/3.0))
##
##    # Winkel testen
##    assert(Vector(5,0,0).angle(Vector(0,3,0)) == math.pi/2)
##    assert(Vector(5,0,0).angle(Vector(3,3,0)) == math.pi/4)
##    
##    # Reflexion testen
##    assert(Vector(1,1,0).reflect(Vector(1,0,0)) == Vector(1,-1,0))
##    reflected = v2.reflect(v)
##    assert(math.fabs(v2.angle(v) - reflected.angle(v)) < 0.000000001)

    print "Alle Tests erfolgreich"
